namespace RAYTRACING {
    /**
     * 场景节点
     */
    export class Mesh {
        private static readonly ConstV3Zero = new Vector3();
        private static readonly ConstV3One = new Vector3(1, 1, 1);
        private static readonly ConstQuatOne = new Quaternion();

        private _rawData: MeshData;
        private _transformData: MeshData;
        /** 原始data数据 */
        public get data() { return this._rawData; }
        public set data(val) {
            if (this._rawData == val) return;
            this._rawData = val;
            if (this._transformData) this._transformData.dispose();
            this._transformData = null;
        }
        /** 变换后的data数据 */
        public get transformData() {
            if (!this._rawData) return;
            if (!this._transformData) {
                this._transformData = new MeshData();
                this._transformData.copy(this._rawData);
            }
            return this._transformData;
        }

        /**
         * 计算 变换
         * @param position 位置变换
         * @param rotation 旋转变换
         * @param scale 缩放变换
         * @returns 
         */
        public calcTransform(position?: Vector3, rotation?: Quaternion, scale?: Vector3) {
            if (!this._rawData) return;
            let tD = this.transformData;
            let rawD = this._rawData;

            let needScale = scale && !scale.equa(Mesh.ConstV3One);
            let needRotation = rotation && !rotation.equa(Mesh.ConstQuatOne);
            let needPosition = position && !position.equa(Mesh.ConstV3Zero);
            if (!needScale && !needRotation && !needPosition) return;

            //顶点变换计算
            let pArr = rawD.posBuf;
            let tPArr = tD.posBuf;
            for (let i = 0, len = pArr.length; i < len; i++) {
                let p = pArr[i];
                let tP = tPArr[i]
                //缩放
                if (needScale) Vector3.ScaleByV3(p, scale, tP);
                //旋转
                if (needRotation) Quaternion.TransformVec3(rotation, tP, tP);
                //位移
                if (needPosition) Vector3.Add(tP, position, tP);
                //后期优化可改成矩阵加速计算
            }

            //法线计算
            tD.calcNormal();
        }
    }


    export class MeshData implements IDispose {
        private static readonly helpV3 = new Vector3();
        private static readonly helpV3_1 = new Vector3();

        /** 三角面的每个顶点索引列表  */
        indexBuf: number[];
        /** 每个顶点坐标列表 */
        posBuf: Vector3[];
        /** 每个顶点的UV坐标列表 */
        uvBuf: Vector2[];
        /** 每个3三角面的法方向向量列表 */
        normalBuf: Vector3[];

        /** 计算所有的法线 */
        public calcNormal() {
            if (!this.indexBuf || !this.posBuf) return;
            let nBuf: Vector3[] = this.normalBuf = [];
            let idxBuf = this.indexBuf;
            let posBuf = this.posBuf;
            //
            let faceCount = idxBuf.length / 3;
            nBuf.length = faceCount;
            for (let i = 0; i < faceCount; i++) {
                let n = nBuf[i];
                if (n == null) nBuf[i] = n = new Vector3();
                let pIdx = i * 3;
                let p0 = posBuf[idxBuf[pIdx]];
                let p1 = posBuf[idxBuf[pIdx + 1]];
                let p2 = posBuf[idxBuf[pIdx + 2]];
                let edge1 = MeshData.helpV3;
                let edge2 = MeshData.helpV3_1;
                Vector3.Sub(p1, p0, edge1);
                Vector3.Sub(p2, p0, edge2);
                Vector3.Cross(edge1, edge2, n);
                Vector3.Normalize(n, n);
            }
        }

        public copy(data: MeshData) {
            if (!data) return;
            for (let k in data) {
                let obj = data[k];
                if (typeof (obj) == 'function') continue;
                if (obj.length != null) {
                    let arr = obj as any[];
                    let myArr = this[k];
                    if (myArr == null) this[k] = myArr = [];
                    myArr.length = arr.length;
                    arr.forEach((val, i) => {
                        if (val.clone) {
                            //可以 克隆对象
                            let cVal = val as IClone;
                            let myVal = myArr[i] as IClone;
                            if (myVal == null) myArr[i] = myVal = cVal.clone();
                            else myVal.copy(cVal);
                        } else {
                            myArr[i] = val; //number
                        }
                    });
                }
            }
        }

        public dispose() {
            this.indexBuf = null;
            this.posBuf = null;
            this.uvBuf = null;
            this.normalBuf = null;
        }
    }
}